package com.cancer.common.util;

import org.quartz.CronTrigger;
import org.quartz.Scheduler;
import org.quartz.TriggerKey;
import org.quartz.impl.JobDetailImpl;
import org.quartz.impl.triggers.CronTriggerImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.quartz.CronTriggerFactoryBean;
import org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean;
import org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean.MethodInvokingJob;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import com.cancer.common.web.ApplicationContextUtils;


/**
 * 定时任务工具类
 * 
 * @company GeekPlus
 * @project beetle
 * @author 刘俊
 * @date 2016年11月25日
 * @since 1.0.0
 */
public class SpringQuartzUtil {
	private static Logger logger = LoggerFactory.getLogger(SpringQuartzUtil.class);
	private static Scheduler scheduler;
	
	public static synchronized Scheduler getScheduler() {
		if (scheduler == null) {
			scheduler = ApplicationContextUtils.getBean(SchedulerFactoryBean.class).getScheduler();
		}
		return scheduler;
	}
	
	public static void setScheduler(Scheduler scheduler) {
		SpringQuartzUtil.scheduler = scheduler;
	}

	public static TriggerKey getTriggerKey(String triggerName) {

		return TriggerKey.triggerKey(triggerName, Scheduler.DEFAULT_GROUP);
	}

	/**
	 * 配置任务计划
	 * 
	 * @author guojianxia 2016年9月1日
	 * @param triggerBean
	 * @return
	 */
	public static boolean updateCronTrigger(QuartzCronTriggerBean triggerBean) {
		boolean result = false;

		try {
			TriggerKey triggerKey = getTriggerKey(triggerBean.getTriggerName());
			CronTriggerImpl trigger = (CronTriggerImpl) getScheduler().getTrigger(triggerKey);
			// 如果计划任务已存在则调用修改方法
			if (trigger != null) {
				// 判断任务是否可用
				if (triggerBean.getStatus() == 1) {
					// 更新cron定时表达式
					if (!trigger.getCronExpression().equalsIgnoreCase(triggerBean.getTriggerExpression())) {
						logger.info("更新{}计划任务, 原值: {}, 新值: {}", triggerBean.getTriggerName(), trigger.getCronExpression(),
								triggerBean.getTriggerExpression());
						trigger.setCronExpression(triggerBean.getTriggerExpression());
						scheduler.rescheduleJob(triggerKey, trigger);
					}

//					boolean needReschedule = false;
//					JobDetailImpl jobImpl = (JobDetailImpl) getScheduler().getJobDetail(trigger.getJobKey());
//					boolean concurrent = jobImpl.getJobClass().equals(MethodInvokingJob.class) ? true : false;
//					if (concurrent != triggerBean.isConcurrent()) {
//						jobImpl.setJobClass(triggerBean.isConcurrent() ? MethodInvokingJob.class : StatefulMethodInvokingJob.class);
//						needReschedule = true;
//					}
//					if (!mjdfb.getTargetMethod().equals(triggerBean.getTargetMethodName())) {
//						mjdfb.setTargetMethod(triggerBean.getTargetMethodName());
//						needReschedule = true;
//					}
//					if (!mjdfb.getTargetClass().getName().equals(triggerBean.getTargetClassName())) {
//						mjdfb.setTargetObject(Class.forName(triggerBean.getTargetClassName()).newInstance());
//						needReschedule = true;
//					}
					// 更新job属性
					// 注意：如果不加&，获取到的将是JobDetail的一个实现，而不是MethodInvokingJobDetailFactoryBean
					MethodInvokingJobDetailFactoryBean mjdfb = ApplicationContextUtils.getBean("&" + triggerBean.getJobDetailName());
					if (mjdfb != null) {
						mjdfb.setName(triggerBean.getJobDetailName());
						// Class<?> jobClass = (this.concurrent ? MethodInvokingJob.class : StatefulMethodInvokingJob.class);
						boolean needReschedule = false;
//						boolean concurrent = jobImpl.getJobClass().equals(MethodInvokingJob.class) ? true : false;
						JobDetailImpl jobImpl = (JobDetailImpl) getScheduler().getJobDetail(trigger.getJobKey());
						boolean concurrent = jobImpl.getJobClass().equals(MethodInvokingJob.class) ? true : false;
						if (concurrent != triggerBean.isConcurrent()) {
							mjdfb.setConcurrent(triggerBean.isConcurrent());
//							needReschedule = true; // TODO 暂不支持修改是否允许并发
						}
						if (!mjdfb.getTargetMethod().equals(triggerBean.getTargetMethodName())) {
							mjdfb.setTargetMethod(triggerBean.getTargetMethodName());
							needReschedule = true;
						}
						if (!mjdfb.getTargetClass().getName().equals(triggerBean.getTargetClassName())) {
							mjdfb.setTargetObject(Class.forName(triggerBean.getTargetClassName()).newInstance());
							needReschedule = true;
						}
						if (needReschedule) {
							// 停止触发器，移除触发器，删除任务
							getScheduler().pauseTrigger(triggerKey);
							getScheduler().unscheduleJob(triggerKey);
							getScheduler().deleteJob(trigger.getJobKey());
							createCronTrigger(triggerBean, triggerKey);
						}
					}
					
				} else {
					// 停止触发器，移除触发器，删除任务
					getScheduler().pauseTrigger(triggerKey);
					getScheduler().unscheduleJob(triggerKey);
					getScheduler().deleteJob(trigger.getJobKey());
					logger.info("删除{}计划任务。", triggerBean.getTriggerName());
				}
			} else {
				// 如果计划任务不存在并且数据库里的任务状态为可用时,则创建计划任务
				if (triggerBean.getStatus() == 1) {
					createCronTrigger(triggerBean, triggerKey);
				}
			}

			result = true;
		} catch (Exception e) {
			e.printStackTrace();
			logger.error("配置任务计划失败！产生异常：{}", e);
		}

		return result;
	}

	/**
	 * 创建或添加新的计划任务
	 * 
	 * @param triggerBean
	 *        计划任务配置对象
	 * @throws Exception
	 */
	public static void createCronTrigger(QuartzCronTriggerBean triggerBean, TriggerKey triggerKey) throws Exception {
		// 新建一个基于Spring的管理Job类
		MethodInvokingJobDetailFactoryBean mjdfb = new MethodInvokingJobDetailFactoryBean();
		// 设置Job名称
		mjdfb.setName(triggerBean.getJobDetailName());
		// 根据任务类是否为Spring定义的Bean来设置任务类
		if (triggerBean.isSpringBean()) {
//			Object obj = ApplicationContextUtils.getBean(triggerBean.getTargetClassName());
			Object obj = ApplicationContextUtils.getBean(Class.forName(triggerBean.getTargetClassName()));
			mjdfb.setTargetObject(obj != null ? obj : Class.forName(triggerBean.getTargetClassName()).newInstance());
		} else {
			mjdfb.setTargetObject(Class.forName(triggerBean.getTargetClassName()).newInstance());
		}
		// 设置任务方法
		mjdfb.setTargetMethod(triggerBean.getTargetMethodName());
		// 设置是否并发启动任务
		mjdfb.setConcurrent(triggerBean.isConcurrent());
		// 将管理Job类提交到计划管理类
		mjdfb.afterPropertiesSet();
		// 将Spring的管理Job类转为Quartz管理Job类
		JobDetailImpl jobDetail = new JobDetailImpl();
		jobDetail = (JobDetailImpl) mjdfb.getObject();
		jobDetail.setName(triggerBean.getJobDetailName());

		// 将Job添加到管理类
		getScheduler().addJob(jobDetail, true);
		// 新一个基于Spring的时间类
		CronTriggerFactoryBean c = new CronTriggerFactoryBean();
		c.setCronExpression(triggerBean.getTriggerExpression());
		c.setName(triggerBean.getTriggerName());
		// 注入Job
		c.setJobDetail(jobDetail);
		c.afterPropertiesSet();
		// 注入到管理类
		CronTrigger trigger = c.getObject();
		getScheduler().scheduleJob(trigger);

		// 刷新管理类
		getScheduler().rescheduleJob(triggerKey, trigger);
		logger.info("新建" + triggerBean.getTriggerName() + "计划任务");
	}

	public static interface QuartzCronTriggerBean {
		/** 如果计划任务不存在并且数据库里的任务状态为可用时,则创建计划任务 1为不存在 */
		public int getStatus();

		/** 触发器名称 */
		public String getTriggerName();
		/** 触发器cron表达式 */
		public String getTriggerExpression();
		/** 触发器引用的job */
		public String getJobDetailName();
		/** 任务类名 */
		public String getTargetClassName();
		/** 类名对应的方法名 */
		public String getTargetMethodName();
		/** 是否允许并发启动任务 */
		public boolean isConcurrent();
		/** 是否允许是已经存在的bean */
		public boolean isSpringBean();
	}
}
